#pragma once

#include <boost/function.hpp>
#include <boostext/smart_ptr.hpp>

#include <engine.core/noncopyable.hpp>
#include <engine.core/math.hpp>
#include <engine.core/vertices.hpp>

#include "exports.hpp"
#include "ActorDesc.hpp"

#pragma warning (push)
#pragma warning (disable: 4251)




/**
 * This structure is returned from an actor to indicate that
 * a raycast test did hit the actor. Both members are filled with
 * the appropriate information (in worldspace).
 */
struct ENGINE_PHYSICS_API ActorRayCast
{
	float_t   parameter;
	vector3   position;
	vector3   normal;
};
///////////////////////////////////////////////////////////////////////////////////////////////////




/**
 * This is the interface for a PhysicsActor.
 * It is modeled from possibly several geometric shapes, has a set of properties (like
 * mass, velocity, etc...) and can interact with other actors in the scene (when desired).
 */
class ENGINE_PHYSICS_API PhysicsActor
	: public noncopyable
	, public boostext::enable_smart_from_this<PhysicsActor, boostext::throw_check>
{
public:

	typedef boost::optional<ActorRayCast>          RayCastResult;
	typedef boost::function<void(PhysicsActor*)>   notify_t;

public:

	PhysicsActor();
	virtual ~PhysicsActor();



	/**
	 * Get access to the actor's entire state.
	 */
	virtual const ActorKinematics& state() const = 0;

	/**
	 * Get the mass of the actor.
	 */
	virtual float_t mass() const = 0;

	/**
	 * Get the position of the actor.
	 */
	virtual vector3 position() const = 0;

	/**
	 * Set the position of the actor.
	 */
	virtual void setPosition(const vector3& p) = 0;

	/**
	 * Get the rotation of the actor.
	 */
	virtual matrix3x3 rotation() const = 0;

	/**
	 * Set the rotation of the actor.
	 */
	virtual void setRotation(const matrix3x3&) = 0;

	/**
	 * Get the transformation (rotation and position) of the actor.
	 */
	virtual matrix4x4 transform() const = 0;

	/**
	 * Set the transformation (rotation and position) of the actor.
	 */
	virtual void setTransform(const matrix4x4&) = 0;



	/**
	 * Get the velocity of this actor.
	 * That's the velocity of the center of mass.
	 *
	 * @note   the direction of the velocity is given in world space
	 */
	virtual vector3 velocity() const = 0;

	/**
	 * Set the velocity of this actor.
	 * That's the velocity of the center of mass.
	 *
	 * @note   the direction must be given in world space
	 */
	virtual void setVelocity(const vector3& v) = 0;

	/**
	 * Add a velocity to this actor's velocity.
	 * That's the velocity of the center of mass.
	 *
	 * @note   the direction must be given in world space
	 */
	virtual void addVelocity(const vector3& v) = 0;



	/**
	 * Get the impulse of this actor.
	 *
	 * @note   the direction is given in world space
	 */
	virtual vector3 impulse() const = 0;

	/**
	 * Set the impulse of this actor.
	 *
	 * @param p   a vector who's length describes the amount of impulse, while pointing in the direction of the impulse
	 *
	 * @note   this vector is assumed to be in world space
	 */
	virtual void setImpulse(const vector3& p) = 0;

	/**
	 * Add an impulse at this actor's current impulse.
	 *
	 * @param p   a vector who's length describes the amount of impulse, while pointing in the direction of the impulse
	 *
	 * @note   this vector is assumed to be in world space
	 */
	virtual void addImpulse(const vector3& p) = 0;



	/**
	 * Apply a force to the center of mass of this actor.
	 *
	 * @param f   a vector who's length describes the amount of force, while pointing in the direction of the force
	 *
	 * @note   this vector is assumed to be in world space
	 */
	virtual void addForce(const vector3& f) = 0;

	/**
	 * Apply a force to the point 'p' of this actor.
	 *
	 * @param p   a position that describes where this force acts on the actor
	 * @param f   a vector who's length describes the amount of force, while pointing in the direction of the force
	 *
	 * @note   both the position and vector are assumed to be in world space
	 */
	virtual void addForce(const vector3& p, const vector3& f) = 0;



	/**
	 * Do a raycast (in WORLD-space) against all shapes from this actor
	 * and then return the point of intersection, as well as the normal of
	 * the face that was intersected by the ray.
	 */
	virtual RayCastResult rayCast(const ray& ray_) const = 0;



	/**
	 * Fill the following vertex- & indexlist with data about this actor, to
	 * be able to visualize it.
	 *
	 * @param tmpVertices   temporary buffer that is used throughout the creation of the mesh data, used to minimize the number of memory allocations
	 * @param tmpIndices    temporary buffer that is used throughout the creation of the mesh data, used to minimize the number of memory allocations
	 */
	virtual void debugRender(MeshVertices& vertices, Indices32 &indices, MeshVertices& tmpVertices = MeshVertices(), Indices32& tmpIndices = Indices32()) const = 0;
};
///////////////////////////////////////////////////////////////////////////////////////////////////

DEFINE_LOOSE_PTR(PhysicsActor);

#pragma warning (pop)
